/******************************************************************************* * Copyright (c) 2000, 2006 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.ui.examples.readmetool; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.Hashtable; import java.util.Vector; import org.eclipse.core.resources.IFile; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IAdaptable; /** * This class is a simple parser implementing the IReadmeFileParser * interface. It parses a Readme file into sections based on the * existence of numbered section tags in the input. A line beginning * with a number followed by a dot will be taken as a section indicator * (for example, 1., 2., or 12.). * As well, a line beginning with a subsection-style series of numbers * will also be taken as a section indicator, and can be used to * indicate subsections (for example, 1.1, or 1.1.12). */ public class DefaultSectionsParser implements IReadmeFileParser { /** * Returns the mark element that is the logical parent * of the given mark number. Each dot in a mark number * represents a parent-child separation. For example, * the parent of 1.2 is 1, the parent of 1.4.1 is 1.4. * Returns null if there is no appropriate parent. */ protected IAdaptable getParent(Hashtable<String, MarkElement> toc, String number) { int lastDot = number.lastIndexOf('.'); if (lastDot < 0) return null; String parentNumber = number.substring(0, lastDot); return toc.get(parentNumber); } /** * Returns a string containing the contents of the given * file. Returns an empty string if there were any errors * reading the file. */ protected String getText(IFile file) { try { InputStream in = file.getContents(); ByteArrayOutputStream out = new ByteArrayOutputStream(); byte[] buf = new byte[1024]; int read = in.read(buf); while (read > 0) { out.write(buf, 0, read); read = in.read(buf); } return out.toString(); } catch (CoreException e) { // do nothing } catch (IOException e) { // do nothing } return ""; //$NON-NLS-1$ } /** * Parses the input given by the argument. * * @param file the element containing the input text * @return an element collection representing the parsed input */ @Override public MarkElement[] parse(IFile file) { Hashtable<String, MarkElement> markTable = new Hashtable<>(40); Vector<MarkElement> topLevel = new Vector<>(); String s = getText(file); int start = 0; int end = -1; int lineno = 0; int lastlineno = 0; MarkElement lastme = null; int ix; // parse content for headings ix = s.indexOf('\n', start); while (ix != -1) { start = end + 1; end = ix = s.indexOf('\n', start); lineno++; if (ix != -1) { // skip blanks while (s.charAt(start) == ' ' || s.charAt(start) == '\t') { start++; } if (Character.isDigit(s.charAt(start))) { if (lastme != null) { lastme.setNumberOfLines(lineno - lastlineno - 1); } lastlineno = lineno; String markName = parseHeading(s, start, end); //get the parent mark, if any. String markNumber = parseNumber(markName); IAdaptable parent = getParent(markTable, markNumber); if (parent == null) parent = file; MarkElement me = new MarkElement(parent, markName, start, end - start); lastme = me; markTable.put(markNumber, me); if (parent == file) { topLevel.add(me); } } } } if (lastme != null) { // set the number of lines for the last section lastme.setNumberOfLines(lineno - lastlineno - 1); } MarkElement[] results = new MarkElement[topLevel.size()]; topLevel.copyInto(results); return results; } /** * Creates a section name from the buffer and trims trailing * space characters. * * @param buffer the string from which to create the section name * @param start the start index * @param end the end index * @return a section name */ private String parseHeading(String buffer, int start, int end) { while (Character.isWhitespace(buffer.charAt(end - 1)) && end > start) { end--; } return buffer.substring(start, end); } /** * Returns the number for this heading. A heading consists * of a number (an arbitrary string of numbers and dots), followed by * arbitrary text. */ protected String parseNumber(String heading) { int start = 0; int end = heading.length(); char c; do { c = heading.charAt(start++); } while ((c == '.' || Character.isDigit(c)) && start < end); //disregard trailing dots while (heading.charAt(start - 1) == '.' && start > 0) { start--; } return heading.substring(0, start); } }